﻿using System;

namespace Gmantis.Zip.ZLib
{
    internal interface IChecksum
    {
        // Methods
        long checkSum(long value, byte[] buf, int index, int len);
    }

    public class ZStream
    {
        // Fields
        internal IChecksum _adler;
        internal bool _crc32;
        public long adler;
        public int avail_in;
        public int avail_out;
        internal int data_type;
        internal const int DEF_WBITS = 15;
        internal Deflate dstate;
        internal Inflate istate;
        internal const int MAX_WBITS = 15;
        public string msg;
        public byte[] next_in;
        public int next_in_index;
        public byte[] next_out;
        public int next_out_index;
        public long total_in;
        public long total_out;
        internal const int Z_BEST_COMPRESSION = 9;
        internal const int Z_BEST_SPEED = 1;
        public const int Z_BUF_ERROR = -5;
        public const int Z_DATA_ERROR = -3;
        internal const int Z_DEFAULT_COMPRESSION = 6;
        internal const int Z_DEFAULT_STRATEGY = 0;
        public const int Z_ERRNO = -1;
        internal const int Z_FILTERED = 1;
        internal const int Z_FINISH = 4;
        internal const int Z_FULL_FLUSH = 3;
        internal const int Z_HUFFMAN_ONLY = 2;
        public const int Z_MEM_ERROR = -4;
        public const int Z_NEED_DICT = 2;
        internal const int Z_NO_COMPRESSION = 0;
        internal const int Z_NO_FLUSH = 0;
        public const int Z_OK = 0;
        internal const int Z_PARTIAL_FLUSH = 1;
        public const int Z_STREAM_END = 1;
        public const int Z_STREAM_ERROR = -2;
        internal const int Z_SYNC_FLUSH = 2;
        public const int Z_VERSION_ERROR = -6;

        // Methods
        public ZStream()
        {
            this._adler = new Adler32();
        }

        public ZStream(bool crc32)
        {
            this._crc32 = crc32;
            if (crc32)
            {
                this._adler = new CRC32();
            }
            else
            {
                this._adler = new Adler32();
            }
        }

        public int deflate(int flush)
        {
            if (this.dstate == null)
            {
                return -2;
            }
            return this.dstate.deflate(this, flush);
        }

        public int deflateEnd()
        {
            if (this.dstate == null)
            {
                return -2;
            }
            int num = this.dstate.deflateEnd();
            this.dstate = null;
            return num;
        }

        public int deflateInit(int level)
        {
            this.dstate = new Deflate();
            return this.dstate.deflateInit(this, level, 15);
        }

        public int deflateInit(int level, int bits)
        {
            this.dstate = new Deflate();
            return this.dstate.deflateInit(this, level, bits);
        }

        public int deflateParams(int level, int strategy)
        {
            if (this.dstate == null)
            {
                return -2;
            }
            return this.dstate.deflateParams(this, level, strategy);
        }

        public int deflateSetDictionary(byte[] dictionary, int dictLength)
        {
            if (this.dstate == null)
            {
                return -2;
            }
            return this.dstate.deflateSetDictionary(this, dictionary, dictLength);
        }

        internal void flush_pending()
        {
            int pending = this.dstate.pending;
            if (pending > this.avail_out)
            {
                pending = this.avail_out;
            }
            if (pending != 0)
            {
                Array.Copy(this.dstate.pending_buf, this.dstate.pending_out, this.next_out, this.next_out_index, pending);
                this.next_out_index += pending;
                this.dstate.pending_out += pending;
                this.total_out += pending;
                this.avail_out -= pending;
                this.dstate.pending -= pending;
                if (this.dstate.pending == 0)
                {
                    this.dstate.pending_out = 0;
                }
            }
        }

        public int inflate(int flush)
        {
            if (this.istate == null)
            {
                return -2;
            }
            return Inflate.inflate(this, flush);
        }

        public int inflateEnd()
        {
            if (this.istate == null)
            {
                return -2;
            }
            int num = this.istate.inflateEnd(this);
            this.istate = null;
            return num;
        }

        public int inflateInit()
        {
            this.istate = new Inflate();
            return this.istate.inflateInit(this, 15);
        }

        public int inflateInit(int bits)
        {
            this.istate = new Inflate();
            return this.istate.inflateInit(this, bits);
        }

        public int inflateSetDictionary(byte[] dictionary, int dictLength)
        {
            if (this.istate == null)
            {
                return -2;
            }
            return Inflate.inflateSetDictionary(this, dictionary, dictLength);
        }

        public int inflateSync()
        {
            if (this.istate == null)
            {
                return -2;
            }
            return this.istate.inflateSync(this);
        }

        internal int read_buf(byte[] buf, int start, int size)
        {
            int len = this.avail_in;
            if (len > size)
            {
                len = size;
            }
            if (len == 0)
            {
                return 0;
            }
            this.avail_in -= len;
            if ((this.dstate.noheader == 0) || this._crc32)
            {
                this.adler = this._adler.checkSum(this.adler, this.next_in, this.next_in_index, len);
            }
            Array.Copy(this.next_in, this.next_in_index, buf, start, len);
            this.next_in_index += len;
            this.total_in += len;
            return len;
        }
    }
}
